﻿

namespace OfficeClip.OpenSource.StyleFix.Drivers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;

    public class ReadabilityRules : StyleCopRules
    {
        internal void SA1101_PrefixLocalCallsWithThis(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1101"))
                {
                    int offset = 0;
                    foreach (SAObject violation in workingLine.Violations.Where(obj => obj.ErrorId == "SA1101"))
                    {
                        workingLine.Line = workingLine.Line.Insert((violation.Column - 1) + offset, "this.");
                        offset += 5;
                    }
                    //// Find the violation
                    //SAObject violation = workingLine.Violations.Find(sao => sao.ErrorId == "SA1101");
                    //string[] arrayWord = violation.Description.Split(" ".ToCharArray());

                    //// The fourth letter happens to be the filename
                    //workingLine.Line = Regex.Replace(
                    //     workingLine.Line,
                    //     string.Format(@"(\s|\(\[)({0})(\W)", arrayWord[3]),
                    //     "$1this.$2$3",
                    //     RegexOptions.None);

                    // Sometimes the same error is shown twice and the this. is repeated... so we need to take one out
                    // return workingLine.Line.Replace("this.this.", "this.");
                }
            }
        }

        internal void SA1115_ParameterMustFollowComma(ref List<SFWorkingLine> workingLines)
        {
            int lineOffset = 0;
            List<SAObject> noViolations = new List<SAObject>();
            for (int i = 0; i < workingLines.Count; i++)
            {
                SFWorkingLine workingLine = workingLines[i];
                workingLine.LineNo += lineOffset;
                if (IsLineViolated(workingLine, "SA1115"))
                {
                    if (i > 0)
                    {
                        if (string.IsNullOrEmpty(workingLines[i - 1].Line.Trim()))
                        {
                            workingLines.RemoveAt(i - 1);
                            lineOffset--;
                            workingLine.LineNo--;
                            //We've removed a line but this line might need more processing
                            i--;
                        }
                    }
                    string line = workingLine.Line;
                    string whitespace = Regex.Match(line, @"\s*").Value;
                    int commentPos = workingLine.Line.IndexOf("//");
                    //insert line break
                    string comment = "";
                    if (commentPos > 0)
                    {
                        comment = workingLine.Line.Substring(commentPos);
                        line = line.Remove(commentPos);
                    }
                    var match = Regex.Match(line, @"(.*\(.+?,)?(.+?,)+(.*)");
                    if (match.Success)
                    {
                        workingLine.Line = match.Groups[1].Value;
                        if (string.IsNullOrEmpty(workingLine.Line.Trim()))
                        {
                            workingLines.RemoveAt(i);
                            lineOffset--;
                            i--;
                        }
                        string newLineText;
                        SFWorkingLine newLine;
                        foreach (Capture Capture in match.Groups[2].Captures)
                        {
                            newLineText = whitespace + Capture.Value.TrimStart();
                            if (!String.IsNullOrEmpty(newLineText.Trim()))
                            {
                                newLine = new SFWorkingLine(workingLine.LineNo + 1, newLineText, false, noViolations);
                                workingLines.Insert(i + 1, newLine);
                                i++;
                                lineOffset++;
                            }
                        }
                        newLineText = whitespace + match.Groups[3].Value.TrimStart() + comment;
                        if (!String.IsNullOrEmpty(newLineText.Trim()))
                        {
                            newLine = new SFWorkingLine(workingLine.LineNo + 1, newLineText, false, noViolations);
                            workingLines.Insert(i + 1, newLine);
                            i++;
                            lineOffset++;
                        }
                    }
                }
            }
        }

        internal void SA1116_SplitParametersMustStartOnLineAfterDeclaration(ref List<SFWorkingLine> workingLines)
        {
            int lineOffset = 0;
            List<SAObject> noViolations = new List<SAObject>();
            for (int i = 0; i < workingLines.Count; i++)
            {
                SFWorkingLine workingLine = workingLines[i];
                if (IsLineViolated(workingLine, "SA1116"))
                {
                    string line = workingLine.Line;
                    string whitespace = Regex.Match(line, @"\s*").Value;
                    int commentPos = workingLine.Line.IndexOf("//");
                    //insert line break
                    string comment = "";
                    if (commentPos > 0)
                    {
                        comment = workingLine.Line.Substring(commentPos);
                        line = line.Remove(commentPos);
                    }
                    var match = Regex.Match(line, @"(.*?\()(.*)");
                    if (match.Success)
                    {
                        workingLine.Line = match.Groups[1].Value;
                        string newLineText = whitespace + "    " + match.Groups[2].Value + comment;
                        SFWorkingLine newLine = new SFWorkingLine(workingLine.LineNo + 1, newLineText, false, noViolations);
                        workingLines.Insert(i + 1, newLine);
                        i++;
                        lineOffset++;
                    }
                }
            }
        }

        internal void SA1119_StatementMustNotUseUnnecessaryParenthesis(ref List<SFWorkingLine> workingLines)
        {
            for (int i = 0; i < workingLines.Count; i++)
            {
                SFWorkingLine workingLine = workingLines[i];
            
                if (IsLineViolated(workingLine, "SA1119"))
                {
                    List<int> charsRemoved = new List<int>();
                    foreach (SAObject violation in workingLine.Violations.Where(sao => sao.ErrorId == "SA1119"))
                    {
                        int start = violation.Column - 1;
                        start -= charsRemoved.Count(j => j < start);
                        var end = CodeUtils.FindMatchingParenthesis(workingLines, i, start);
                        if (end.LineNumber == i)
                        {
                            charsRemoved.Add(start);
                            charsRemoved.Add(end.Position);
                            workingLine.Line = workingLine.Line.Remove(end.Position, 1);
                            workingLine.Line = workingLine.Line.Remove(start, 1);
                        }
                    }
                }
            }
        }

        internal void SA1120_CommentsMustContainText(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1120"))
                {
                    workingLine.Line = Regex.Replace(workingLine.Line, @"(.*)//\s*$", "$1");
                    workingLine.Line = Regex.Replace(workingLine.Line, @"(.*)/\*\s*\*/(.*)", "$1$2");
                }
            }
        }

        internal void SA1121_UseBuiltInTypeAlias(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1121"))
                {
                    SAObject saob = workingLine.Violations.Find(sao => sao.ErrorId == "SA1121");

                    if (saob.Description.IndexOf("'array'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Array", "array").Replace("Array", "array");
                    }

                    if (saob.Description.IndexOf("'bool'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Boolean", "bool").Replace("Boolean", "bool");
                    }

                    if (saob.Description.IndexOf("'byte'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Byte", "byte").Replace("Byte", "byte");
                    }

                    if (saob.Description.IndexOf("'char'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Char", "char").Replace("Char", "char");
                    }

                    if (saob.Description.IndexOf("'decimal'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Decimal", "decimal").Replace("Decimal", "decimal");
                    }

                    if (saob.Description.IndexOf("'double'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Double", "double").Replace("Double", "double");
                    }

                    if (saob.Description.IndexOf("'short'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Int16", "short").Replace("Int16", "short");
                    }

                    if (saob.Description.IndexOf("'int'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Int32", "int").Replace("Int32", "int");
                    }

                    if (saob.Description.IndexOf("'long'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Int64", "long").Replace("Int64", "long");
                    }

                    if (saob.Description.IndexOf("'object'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Object", "object").Replace("Object", "object");
                    }

                    if (saob.Description.IndexOf("'sbyte'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Sbyte", "sbyte").Replace("Sbyte", "sbyte");
                    }

                    if (saob.Description.IndexOf("'single'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Single", "single").Replace("Single", "single");
                    }

                    if (saob.Description.IndexOf("'string'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.String", "string").Replace("String", "string");
                    }

                    if (saob.Description.IndexOf("'ushort'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Ushort", "ushort").Replace("Ushort", "ushort");
                    }

                    if (saob.Description.IndexOf("'uint'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Uint", "uint").Replace("Uint", "uint");
                    }

                    if (saob.Description.IndexOf("'ulong'") > -1)
                    {
                        workingLine.Line = workingLine.Line.Replace("System.Ulong", "ulong").Replace("Ulong", "ulong");
                    }
                }
            }
        }

        internal void SA1126_PrefixCallsCorrectly(ref List<SFWorkingLine> workingLines)
        {

            CodeUtils.CalculateNestingLevels(workingLines);

            List<string> Lines = new List<string>();

            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (workingLine.ClassRoot && ! string.IsNullOrEmpty(workingLine.Line))
                {
                    char firstChar = workingLine.Line.TrimStart().First();
                    if (firstChar != '{' && firstChar != '}')
                        Lines.Add(workingLine.Line);
                }
            }

            Dictionary<string, bool> members = new Dictionary<string, bool>();

            for(int i = 0; i < Lines.Count; i++)
            {
                string line = Lines[i];
                int x = line.IndexOf('(');
                if (x > 0)
                {
                    line = line.Remove(x);
                }
                var lineparts = line.Split(new char[] {' ', ';'}, StringSplitOptions.RemoveEmptyEntries);
                bool isStatic = lineparts.Any(l => l == "static");
                
                if (!members.ContainsKey(lineparts.Last()))
                    members.Add(lineparts.Last(), isStatic);
            }

            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1126"))
                {
                    SAObject saob = workingLine.Violations.Find(sao => sao.ErrorId == "SA1126");

                    string target = saob.Description.Split(' ')[3];
                    if (members.ContainsKey(target))
                    {
                        string typename = "this";
                        if (members[target])
                        {
                            typename = saob.Description.Split(' ')[13];
                        }
                        workingLine.Line = Regex.Replace(workingLine.Line, string.Format(@"([^a-zA-Z.])({0}\W)", target), "$1" + typename + ".$2");
                    }
                }
            }           
        }
    }
}
